﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.IO;
using System.Diagnostics;
using System.Windows.Interop;

namespace KeyboardImageViewer
{
	/// <summary>
	/// Interaction logic for Window1.xaml
	/// </summary>
	public partial class MainWindow : Window
	{
		private bool fullScreen = true;
		private bool overlayInfobar = true;

		private InputMapping inputMapping;
		private bool allowInput = true;

		private ViewerImage currentImage;

		private const int WM_KEYDOWN = 0x100;
		private const int WM_SYSKEYDOWN = 0x0104;

		private IntPtr hwnd;

		public MainWindow()
		{
			InitializeComponent();

			ImageManager.Initialize(this);

			this.inputMapping = new InputMapping(ImageManager.Connection);

			FullscreenStart fullscreenStart = ImageManager.Config.FullscreenStartSetting;
			switch (fullscreenStart)
			{
				case FullscreenStart.RememberLast:
					if (ImageManager.Config.FullscreenStart)
					{
						this.SetFullscreen();
					}
					else
					{
						this.SetWindowed();
					}

					break;
				case FullscreenStart.AlwaysFullscreen:
					this.SetFullscreen();
					break;
				case FullscreenStart.AlwaysWindowed:
					this.SetWindowed();
					break;
				default:
					break;
			}

			if (!ImageManager.Config.OverlayInfobar)
			{
				this.OverlayInfobar = false;
			}

			SystemDisplayManagement.RequireDisplay();
		}

		public void SetPicture(CachedImage image)
		{
			this.informationalText.Visibility = Visibility.Collapsed;
			this.errorText.Visibility = Visibility.Collapsed;

			if (image != null && image.Image != null)
			{
				ViewerImage viewerImage = image.Image;

				if (viewerImage.IsAnimated)
				{
					try
					{
						this.gifImage.GIFSource = Database.GetImageName(ImageManager.Connection, viewerImage.ImageId);
					}
					catch (FileNotFoundException)
					{
						viewerImage.FileExists = false;
					}

					this.backgroundRectangle.Visibility = Visibility.Hidden;
					this.imageGrid.Visibility = Visibility.Hidden;
					this.gifImage.Visibility = Visibility.Visible;
				}
				else
				{
					this.gifImage.GIFSource = null;
					this.gifImage.Visibility = Visibility.Hidden;
					this.backgroundRectangle.Visibility = Visibility.Visible;
					this.imageGrid.Visibility = Visibility.Visible;

					this.imageGrid.Children.Remove(image.UIImage);
					this.imageGrid.Children.Remove(this.backgroundRectangle);
					this.imageGrid.Children.Add(this.backgroundRectangle);

					if (!viewerImage.Corrupted && viewerImage.FileExists)
					{
						this.imageGrid.Children.Add(image.UIImage);
						this.backgroundRectangle.Fill = viewerImage.BgColor;
					}
					else
					{
						this.backgroundRectangle.Fill = Brushes.Black;
					}
				}

				if (!viewerImage.FileExists)
				{
					this.errorText.Text = "File no longer exists.";
					this.errorText.Visibility = Visibility.Visible;
				}

				if (viewerImage.Corrupted)
				{
					this.errorText.Text = "Could not read image.";
					this.errorText.Visibility = Visibility.Visible;
				}

				this.currentImage = viewerImage;
			}
			else
			{
				this.gifImage.Visibility = Visibility.Hidden;
				this.backgroundRectangle.Visibility = Visibility.Hidden;
				this.imageGrid.Visibility = Visibility.Hidden;

				this.gifImage.GIFSource = null;

				this.currentImage = null;
			}

			this.UpdateInfoBar();
		}

		public void ShowNoResults()
		{
			this.gifImage.Visibility = Visibility.Hidden;
			this.backgroundRectangle.Visibility = Visibility.Hidden;
			this.imageGrid.Visibility = Visibility.Hidden;
			this.errorText.Visibility = Visibility.Collapsed;

			this.informationalText.Text = "No image met filter critera.";
			this.informationalText.Visibility = Visibility.Visible;

			this.gifImage.GIFSource = null;

			this.currentImage = null;
			this.UpdateInfoBar();
		}

		public void ShowNoImages()
		{
			this.gifImage.Visibility = Visibility.Hidden;
			this.backgroundRectangle.Visibility = Visibility.Hidden;
			this.imageGrid.Visibility = Visibility.Hidden;
			this.errorText.Visibility = Visibility.Collapsed;

			this.informationalText.Text = "No images present in library.";
			this.informationalText.Visibility = Visibility.Visible;

			this.gifImage.GIFSource = null;

			this.currentImage = null;
			this.UpdateInfoBar();
		}

		public void SetLoadingPicture()
		{
			this.imageGrid.Children.Remove(this.backgroundRectangle);
			this.imageGrid.Children.Add(this.backgroundRectangle);
			this.informationalText.Text = "Loading...";
			this.informationalText.Visibility = Visibility.Visible;
			this.backgroundRectangle.Fill = Brushes.Black;

			this.UpdateInfoBarUserData(ImageListControl.CurrentImageData);
		}

		public Image AddToCache(ViewerImage viewerImage)
		{
			if (!viewerImage.FileExists || viewerImage.IsAnimated || viewerImage.Corrupted)
			{
				return null;
			}

			BitmapImage image = viewerImage.Image;
			Image uiImage = new Image();
			//RenderOptions.SetBitmapScalingMode(uiImage, BitmapScalingMode.LowQuality);
			uiImage.Source = image;

			// Sometimes the DPI goes crazy and we need to correct it. This is a workaround, these images will only be
			// displayed correctly initially.
			if (image.DpiX < 2.0 && image.DpiY > 10.0 || image.DpiY < 2.0 && image.DpiX > 10.0)
			{
				double holderAspectRatio = this.ImageAreaAspectRatio;
				double imageAspectRatio;
				if (viewerImage.Rotation == 0 || viewerImage.Rotation == 180)
				{
					imageAspectRatio = ((double)image.PixelWidth) / image.PixelHeight;
				}
				else
				{
					imageAspectRatio = ((double)image.PixelHeight) / image.PixelWidth;
				}

				uiImage.Stretch = Stretch.Fill;
				if (imageAspectRatio > holderAspectRatio)
				{
					uiImage.Width = this.imageGrid.ActualWidth;
					uiImage.Height = this.imageGrid.ActualWidth / imageAspectRatio;
				}
				else
				{
					uiImage.Width = this.imageGrid.ActualHeight * imageAspectRatio;
					uiImage.Height = this.imageGrid.ActualHeight;
				}
			}

			this.imageGrid.Children.Insert(0, uiImage);

			return uiImage;
		}

		public void RemoveFromCache(Image uiImage)
		{
			if (uiImage != null)
			{
				this.imageGrid.Children.Remove(uiImage);
			}
		}

		public void ClearCache()
		{
			this.imageGrid.Children.RemoveRange(0, this.imageGrid.Children.Count);
		}

		public bool AllowInput
		{
			get { return this.allowInput; }
			set { this.allowInput = value; }
		}

		public void UpdateInfoBar()
		{
			this.infoBarGrid.Visibility = Visibility.Visible;

			if (ImageManager.ImageShowing && this.currentImage != null)
			{
				ImageUserData imageData = ImageListControl.CurrentImageData;

				if (imageData.Tags.Count > 0)
				{
					this.tagsPanel.Visibility = Visibility.Visible;
					this.tagsText.Text = imageData.TagsString;
				}
				else
				{
					this.tagsPanel.Visibility = Visibility.Collapsed;
				}

				this.fileName.Text = ImageManager.Config.SourceDirectories.GetRelativeName(imageData.Name);

				if (ImageManager.Config.TrackDuplicates && this.currentImage.Duplicates != null && this.currentImage.Duplicates.Count > 0)
				{
					this.duplicate.Visibility = Visibility.Visible;
					if (this.currentImage.Duplicates[0].ExactDuplicate)
					{
						this.duplicate.Foreground = new SolidColorBrush(Color.FromRgb(79, 143, 255));
						this.duplicate.Text = "(Duplicate File)";
					}
					else
					{
						this.duplicate.Foreground = Brushes.Aqua;
						this.duplicate.Text = "(Duplicate Image)";
					}
				}
				else
				{
					this.duplicate.Visibility = Visibility.Collapsed;
				}

				if (imageData.Rating > 0)
				{
					this.rating.Visibility = Visibility.Visible;
					this.rating.Text = imageData.Rating.ToString();

					this.rating.Foreground = Utilities.GetColorFromRating(imageData.Rating);
				}
				else
				{
					this.rating.Visibility = Visibility.Collapsed;
				}

				if (ImageListControl.Playing)
				{
					this.playIcon.Visibility = Visibility.Visible;
				}
				else
				{
					this.playIcon.Visibility = Visibility.Collapsed;
				}

				this.viewIndex.Text = (ImageListControl.CurrentIndex + 1) + "/" + ImageListControl.TotalImages;
			}
			else
			{
				this.tagsPanel.Visibility = Visibility.Collapsed;
				this.duplicate.Visibility = Visibility.Collapsed;
				this.rating.Visibility = Visibility.Collapsed;
				this.playIcon.Visibility = Visibility.Collapsed;

				this.fileName.Text = "";

				this.viewIndex.Text = "0/0";
			}

			this.UpdateImageQueryInfo();
		}

		private void UpdateInfoBarUserData(ImageUserData imageData)
		{
			if (imageData.Tags.Count > 0)
			{
				this.tagsPanel.Visibility = Visibility.Visible;
				this.tagsText.Text = imageData.TagsString;
			}
			else
			{
				this.tagsPanel.Visibility = Visibility.Collapsed;
			}

			this.fileName.Text = ImageManager.Config.SourceDirectories.GetRelativeName(imageData.Name);

			this.duplicate.Visibility = Visibility.Collapsed;

			if (imageData.Rating > 0)
			{
				this.rating.Visibility = Visibility.Visible;
				this.rating.Text = imageData.Rating.ToString();

				this.rating.Foreground = Utilities.GetColorFromRating(imageData.Rating);
			}
			else
			{
				this.rating.Visibility = Visibility.Collapsed;
			}

			this.UpdateImageQueryInfo();
		}

		private void UpdateImageQueryInfo()
		{
			if (ImageQuery.ListName != "" && QueryOperations.CurrentListId != ImageManager.Config.StartingListId)
			{
				this.order.Text = "List: " + ImageQuery.ListName;
			}
			else
			{
				if (ImageQuery.Sort == SortType.Modified)
				{
					this.order.Text = "Last Modified";

					if (ImageQuery.Ascending)
					{
						this.order.Text += ": Oldest First";
					}
				}
				else if (ImageQuery.Sort == SortType.Random)
				{
					this.order.Text = "Random";
				}
				else if (ImageQuery.Sort == SortType.Name)
				{
					this.order.Text = "File Name";

					if (!ImageQuery.Ascending)
					{
						this.order.Text += ": Reverse Order";
					}
				}

				List<string> filters = new List<string>();

				int ratingView = ImageQuery.MinRating;
				if (ratingView == -1)
				{
					filters.Add("Unrated");
				}
				else if (ratingView > 0)
				{
					filters.Add(ratingView.ToString() + "+");
				}
				else
				{
					this.filtersText.Text = "";
				}

				if (ImageQuery.UntaggedOnly)
				{
					filters.Add("Untagged");
				}

				if (ImageQuery.Search != "")
				{
					filters.Add("\"" + ImageQuery.Search + "\"");
				}

				if (ImageQuery.CustomClause != "")
				{
					filters.Add("Custom Clause");
				}

				if (filters.Count > 0)
				{
					this.filtersPanel.Visibility = Visibility.Visible;
					this.filtersText.Text = string.Join(", ", filters.ToArray());
				}
				else
				{
					this.filtersPanel.Visibility = Visibility.Collapsed;
				}
			}
		}

		public bool ProgressBarVisible
		{
			get
			{
				return this.indexingProgressPanel.Visibility == Visibility.Visible;
			}
			set
			{
				if (value)
				{
					this.gifImage.Visibility = Visibility.Hidden;
					this.infoBarGrid.Visibility = Visibility.Collapsed;
					this.indexProgress.Value = 0;
					this.indexingProgressPanel.Visibility = Visibility.Visible;
				}
				else
				{
					this.gifImage.Visibility = Visibility.Visible;
					this.indexingProgressPanel.Visibility = Visibility.Collapsed;
				}
			}
		}

		public void ReportProgress(double progress)
		{
			this.indexProgress.Value = progress;
		}

		public bool OverlayInfobar
		{
			set
			{
				if (this.overlayInfobar != value)
				{
					this.overlayInfobar = value;

					if (value)
					{
						Grid.SetRowSpan(this.imageGrid, 2);
						Grid.SetRowSpan(this.gifImage, 2);
						//this.overlayBorder.Visibility = Visibility.Visible;
					}
					else
					{
						Grid.SetRowSpan(this.imageGrid, 1);
						Grid.SetRowSpan(this.gifImage, 1);
						//this.overlayBorder.Visibility = Visibility.Collapsed;
					}
				}
			}
		}

		public bool FullScreen
		{
			get { return this.fullScreen; }
		}

		public double ImageAreaAspectRatio
		{
			get
			{
				return this.imageGrid.ActualWidth / this.imageGrid.ActualHeight;
			}
		}

		private void Window_KeyDown(object sender, KeyEventArgs e)
		{
			bool control = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
			bool alt = Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt);

			if (control)
			{
				this.ExecuteUserAction(this.inputMapping.GetActionFromKeyCtrl(e.Key));
			}
			else if (alt)
			{
				this.ExecuteUserAction(this.inputMapping.GetActionFromKeyAlt(e.SystemKey));
			}
			else
			{
				this.ExecuteUserAction(this.inputMapping.GetActionFromKey(e.Key));
			}
		}

		private void ExecuteUserAction(UserAction action)
		{
			if (!this.allowInput && action != UserAction.ToggleFullscreen && action != UserAction.Quit)
			{
				return;
			}

			switch (action)
			{
				case UserAction.None:
					break;
				case UserAction.ShowHelp:
					EditKeys editKeys = new EditKeys();
					editKeys.Owner = this;
					if (editKeys.ShowDialog() == true)
					{
						this.inputMapping = new InputMapping(ImageManager.Connection);
					}
					break;
				case UserAction.ShowOptions:
					Settings settingsWindow = new Settings(this);
					if (settingsWindow.ShowDialog() == true)
					{
						ImageManager.UpdateSettings(settingsWindow);
					}
					break;
				case UserAction.ReloadFilesFromDisk:
					ImageManager.ReloadFiles();
					break;
				case UserAction.ToggleFullscreen:
					this.ToggleFullscreen();
					break;
				case UserAction.Minimize:
					this.WindowState = WindowState.Minimized;
					break;
				case UserAction.Quit:
					this.Close();
					break;
				case UserAction.RateAs1:
					EditOperations.RateImage(1);
					break;
				case UserAction.RateAs2:
					EditOperations.RateImage(2);
					break;
				case UserAction.RateAs3:
					EditOperations.RateImage(3);
					break;
				case UserAction.RateAs4:
					EditOperations.RateImage(4);
					break;
				case UserAction.RateAs5:
					EditOperations.RateImage(5);
					break;
				case UserAction.ClearRating:
					EditOperations.RateImage(0);
					break;
				case UserAction.Tag:
					if (ImageManager.ImageShowing)
					{
						ImageListControl.StopSlideshowIfRunning();
						EditTagBox tagBox = new EditTagBox(this);
						tagBox.ShowDialog();
					}
					break;
				case UserAction.TagEditMultiple:
					if (ImageManager.ImageShowing)
					{
						EditMultipleTags editMultipleTagBox = new EditMultipleTags(this);
						editMultipleTagBox.ShowDialog();
					}
					break;
				case UserAction.TagRenameOrDelete:
					TagRenameOrDelete batchTag = new TagRenameOrDelete();
					batchTag.Owner = this;
					batchTag.ShowDialog();
					break;
				case UserAction.Rename:
					ImageListControl.StopSlideshowIfRunning();
					this.ShowRename();
					break;
				case UserAction.Move:
					EditOperations.MoveVisibleFiles();
					this.Focus();
					break;
				case UserAction.CopyFiles:
					EditOperations.CopyVisibleFiles();
					break;
				case UserAction.EditCurrentFile:
					ImageListControl.StopSlideshowIfRunning();
					EditOperations.EditCurrentImage();
					break;
				case UserAction.DeleteCurrentFile:
					ImageListControl.DeleteCurrentImage();
					break;
				case UserAction.Import:
					Import import = new Import(this);
					import.ShowDialog();
					break;
				case UserAction.EditTagDependencies:
					EditTagDependencies dependenciesForm = new EditTagDependencies(this);
					dependenciesForm.ShowDialog();
					break;
				case UserAction.ShowPreviousImage:
					ImageListControl.MoveLeft();
					break;
				case UserAction.ShowNextImage:
					ImageListControl.MoveRight();
					break;
				case UserAction.MoveToFirstImage:
					ImageListControl.MoveToStart();
					break;
				case UserAction.MoveToLastImage:
					ImageListControl.MoveToEnd();
					break;
				case UserAction.PlayStopSlideshow:
					ImageListControl.ToggleSlideshow();
					break;
				case UserAction.ShowLists:
					ViewLists viewLists = new ViewLists();
					viewLists.Owner = this;
					viewLists.ShowDialog();
					break;
				case UserAction.ChangeOrder:
					QueryOperations.ChangeOrder();
					break;
				case UserAction.Search:
					this.ShowSearch();
					break;
				case UserAction.ClearSearch:
					QueryOperations.ClearSearch();
					break;
				case UserAction.ShowRating1OrGreater:
					QueryOperations.SetRatingView(1);
					break;
				case UserAction.ShowRating2OrGreater:
					QueryOperations.SetRatingView(2);
					break;
				case UserAction.ShowRating3OrGreater:
					QueryOperations.SetRatingView(3);
					break;
				case UserAction.ShowRating4OrGreater:
					QueryOperations.SetRatingView(4);
					break;
				case UserAction.ShowRating5OrGreater:
					QueryOperations.SetRatingView(5);
					break;
				case UserAction.ClearRatingFilter:
					QueryOperations.SetRatingView(0);
					break;
				case UserAction.ShowOnlyUnrated:
					QueryOperations.SetRatingView(-1);
					break;
				case UserAction.ShowOnlyUntagged:
					QueryOperations.ToggleUntagged();
					break;
				case UserAction.ShowDuplicates:
					if (ImageManager.Config.TrackDuplicates && ImageManager.ImageShowing && this.currentImage != null && this.currentImage.Duplicates != null && this.currentImage.Duplicates.Count > 0)
					{
						ImageListControl.StopSlideshowIfRunning();
						Duplicates duplicates = new Duplicates(this.currentImage.Duplicates);
						duplicates.Owner = this;
						duplicates.ShowDialog();
						ImageListControl.RemoveDeletedImageData(duplicates.DeletedImages);
					}
					break;
				case UserAction.ShowAllDuplicates:
					if (!ImageManager.Config.TrackDuplicates)
					{
						MessageBox.Show("To show all duplicates, you have to enable duplicate tracking in Settings first.");
					}
					else
					{
						AllDuplicates duplicates = new AllDuplicates();
						duplicates.Owner = this;
						duplicates.ShowDialog();
						ImageListControl.RemoveDeletedImageData(duplicates.DeletedImages);
					}
					break;
				default:
					Debug.Assert(false, "Unknown command encountered");
					break;
			}
		}

		private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
		{
			int clicks = e.Delta / -120;

			if (clicks > 0)
			{
				this.ExecuteUserAction(this.inputMapping.GetActionFromMouseWheel(MouseWheelAction.Down));
			}
			else if (clicks < 0)
			{
				this.ExecuteUserAction(this.inputMapping.GetActionFromMouseWheel(MouseWheelAction.Up));
			}
		}

		private void Window_MouseDown(object sender, MouseButtonEventArgs e)
		{
			Point point = Mouse.GetPosition(this);

			var userAction = this.inputMapping.GetActionFromMouseButton(e.ChangedButton);

			if (userAction == UserAction.None && e.ChangedButton == MouseButton.Left)
			{
				if (point.X < this.ActualWidth / 2)
				{
					this.ExecuteUserAction(UserAction.ShowPreviousImage);
				}
				else
				{
					this.ExecuteUserAction(UserAction.ShowNextImage);
				}
			}
			else
			{
				this.ExecuteUserAction(userAction);
			}
		}

		private void ToggleFullscreen()
		{
			if (this.fullScreen)
			{
				this.SetWindowed();
			}
			else
			{
				ImageManager.SaveWindowSize();

				this.SetFullscreen();
			}
		}

		private void SetFullscreen()
		{
			this.fullScreen = true;

			this.Cursor = Cursors.None;
			this.WindowStyle = WindowStyle.None;
			this.ResizeMode = ResizeMode.NoResize;
			this.WindowState = WindowState.Maximized;
		}

		private void SetWindowed()
		{
			this.fullScreen = false;

			this.Cursor = Cursors.Arrow;
			this.WindowStyle = WindowStyle.SingleBorderWindow;
			this.ResizeMode = ResizeMode.CanResize;
			this.WindowState = WindowState.Normal;
		}

		private void ShowSearch()
		{
			SearchBox searchBox = new SearchBox(this);
			searchBox.ShowDialog();
		}

		private void ShowRename()
		{
			if (ImageManager.ImageShowing)
			{
				RenameBox renameBox = new RenameBox(this);
				renameBox.ShowDialog();
			}
		}

		protected override void OnSourceInitialized(EventArgs e)
		{
			base.OnSourceInitialized(e);
			this.hwnd = new WindowInteropHelper(this).Handle;
		}

		private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			if (this.WindowState == WindowState.Normal)
			{
				ImageManager.SaveWindowSize();
			}

			if (ImageManager.Config.FullscreenStartSetting == FullscreenStart.RememberLast)
			{
				ImageManager.Config.FullscreenStart = this.fullScreen;
			}

			UpdateHandler.PromptToApplyUpdate();
		}
	}
}
